﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Configuration;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Web;
using CodeSandbox;
using CodeSandbox.Common;
using CodeSandbox.Contracts;
using CodeSandbox.Controllers;
using CodeSandbox.Models;


namespace CodeSandbox
{
    /// <summary>
    /// A Global class responsible to execute the provided code under sandboxed environment. 
    /// </summary>
    //[DebuggerStepThrough]
    public static class Sandbox
    {
        private static readonly object LockObject = new object();
        private static readonly Guid ContextValue;
        private static List<ISandbox> _activeSandboxes;
        private static bool _supressOutput;

        static Sandbox()
        {
            ContextValue = Guid.NewGuid();
            ActiveSandboxes = new List<ISandbox>();
            Company = ConfigurationManager.AppSettings["Company"];
            Division = ConfigurationManager.AppSettings["Division"];
            Application = ConfigurationManager.AppSettings["Application"];
            RootAssemblyPrefix = ConfigurationManager.AppSettings["RootPrefix"];
            LocalLogFile = ConfigurationManager.AppSettings["logFile"];
            RolloverSizeInMb = ConfigurationManager.AppSettings["RolloverSizeInMb"] == null ? 0 : ConfigurationManager.AppSettings["RolloverSizeInMb"].MapTo<int>();
            LogMode logMode;
            if (Enum.TryParse(ConfigurationManager.AppSettings["LogMode"], true, out logMode)) LogMode = logMode;
            SupressOutput = ConfigurationManager.AppSettings["SupressOutput"] != null &&
                            (ConfigurationManager.AppSettings["SupressOutput"].ToLower() == "true" ||
                             ConfigurationManager.AppSettings["SupressOutput"] == "1");
        }


        /// <summary>
        /// Represents a unique indetifier for the current executing session.
        /// </summary>
        public static Guid ContextId
        {
            get
            {
                lock (LockObject)
                {
                    return ContextValue;
                }
            }
        }

        #region Delegates with returns without logicOnException

        private static TReturn Run
            <TReturn, TKnownException1, TKnownException2, TKnownException3, TKnownException4, TKnownException5>(
                Func<ISandbox, TReturn> logicToRun, string callerFile, string callerName, int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
            where TKnownException3 : Exception
            where TKnownException4 : Exception
            where TKnownException5 : Exception
        {
            return
                Run
                    <TReturn, TKnownException1, TKnownException2, TKnownException3, TKnownException4, TKnownException5>(
                        logicToRun, null, callerFile, callerName, callerLine);
        }

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TReturn">Return Type</typeparam>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TKnownException2">Second known exception</typeparam>
        /// <typeparam name="TKnownException3">Third known exception</typeparam>
        /// <typeparam name="TKnownException4">Forth known exception</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        public static TReturn Run
            <TReturn, TKnownException1, TKnownException2, TKnownException3, TKnownException4>(
                Func<ISandbox, TReturn> logicToRun, [CallerFilePath] string callerFile = @"N/A",
                [CallerMemberName] string callerName = @"N/A", [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
            where TKnownException3 : Exception
            where TKnownException4 : Exception
        {
            return
                Run
                    <TReturn, TKnownException1, TKnownException2, TKnownException3, TKnownException4, IgnoreException>(
                        logicToRun, null, callerFile, callerName, callerLine);
        }

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TReturn">Return Type</typeparam>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TKnownException2">Second known exception</typeparam>
        /// <typeparam name="TKnownException3">Third known exception</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        public static TReturn Run
            <TReturn, TKnownException1, TKnownException2, TKnownException3>(
                Func<ISandbox, TReturn> logicToRun, [CallerFilePath] string callerFile = @"N/A",
                [CallerMemberName] string callerName = @"N/A", [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
            where TKnownException3 : Exception
        {
            return
                Run
                    <TReturn, TKnownException1, TKnownException2, TKnownException3, IgnoreException, IgnoreException>(
                        logicToRun, null, callerFile, callerName, callerLine);
        }

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TReturn">Return Type</typeparam>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TKnownException2">Second known exception</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        public static TReturn Run
            <TReturn, TKnownException1, TKnownException2>(
                Func<ISandbox, TReturn> logicToRun, [CallerFilePath] string callerFile = @"N/A",
                [CallerMemberName] string callerName = @"N/A", [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
        {
            return
                Run
                    <TReturn, TKnownException1, TKnownException2, IgnoreException, IgnoreException, IgnoreException>(
                        logicToRun, null, callerFile, callerName, callerLine);
        }


        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TReturn">Return Type</typeparam>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        public static TReturn Run
            <TReturn, TKnownException1>(
                Func<ISandbox, TReturn> logicToRun, [CallerFilePath] string callerFile = @"N/A",
                [CallerMemberName] string callerName = @"N/A", [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
        {
            return
                Run
                    <TReturn, TKnownException1, IgnoreException, IgnoreException, IgnoreException, IgnoreException>(
                        logicToRun, null, callerFile, callerName, callerLine);
        }


        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TReturn">Return Type</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        public static TReturn Run
            <TReturn>(
                Func<ISandbox, TReturn> logicToRun, [CallerFilePath] string callerFile = @"N/A",
                [CallerMemberName] string callerName = @"N/A", [CallerLineNumber] int callerLine = 0)
        {
            return
                Run
                    <TReturn, IgnoreException, IgnoreException, IgnoreException, IgnoreException, IgnoreException>(
                        logicToRun, null, callerFile, callerName, callerLine);
        }

        #endregion

        #region Delegates without return without logicOnException

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TKnownException2">Second known exception</typeparam>
        /// <typeparam name="TKnownException3">Third known exception</typeparam>
        /// <typeparam name="TKnownException4">Forth known exception</typeparam>
        /// <typeparam name="TKnownException5">Fifth known exception</typeparam>
        public static void Run
            <TKnownException1, TKnownException2, TKnownException3, TKnownException4, TKnownException5>(
                Action<ISandbox> logicToRun, [CallerFilePath] string callerFile = @"N/A",
                [CallerMemberName] string callerName = @"N/A", [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
            where TKnownException3 : Exception
            where TKnownException4 : Exception
            where TKnownException5 : Exception
        {
            Run<bool, TKnownException1, TKnownException2, TKnownException3, TKnownException4, TKnownException5>(
                sandbox =>
                {
                    logicToRun(sandbox);
                    return true;
                }, null, callerFile, callerName, callerLine);
        }

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TKnownException2">Second known exception</typeparam>
        /// <typeparam name="TKnownException3">Third known exception</typeparam>
        /// <typeparam name="TKnownException4">Forth known exception</typeparam>
        public static void Run
            <TKnownException1, TKnownException2, TKnownException3, TKnownException4>(
                Action<ISandbox> logicToRun, [CallerFilePath] string callerFile = @"N/A",
                [CallerMemberName] string callerName = @"N/A", [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
            where TKnownException3 : Exception
            where TKnownException4 : Exception
        {
            Run
                <bool, TKnownException1, TKnownException2, TKnownException3, TKnownException4, IgnoreException>(
                    sandbox =>
                    {
                        logicToRun(sandbox);
                        return true;
                    }, null, callerFile, callerName, callerLine);
        }


        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TKnownException2">Second known exception</typeparam>
        /// <typeparam name="TKnownException3">Third known exception</typeparam>
        public static void Run
            <TKnownException1, TKnownException2, TKnownException3>(
                Action<ISandbox> logicToRun, [CallerFilePath] string callerFile = @"N/A",
                [CallerMemberName] string callerName = @"N/A", [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
            where TKnownException3 : Exception
        {
            Run
                <bool, TKnownException1, TKnownException2, TKnownException3, IgnoreException, IgnoreException>(
                    sandbox =>
                    {
                        logicToRun(sandbox);
                        return true;
                    }, null, callerFile, callerName, callerLine);
        }


        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TKnownException2">Second known exception</typeparam>
        public static void Run
            <TKnownException1, TKnownException2>(
                Action<ISandbox> logicToRun, [CallerFilePath] string callerFile = @"N/A",
                [CallerMemberName] string callerName = @"N/A", [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
        {
            Run
                <bool, TKnownException1, TKnownException2, IgnoreException, IgnoreException, IgnoreException>(sandbox =>
                {
                    logicToRun(sandbox);
                    return true;
                }, null, callerFile, callerName, callerLine);
        }


        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        public static void Run
            <TKnownException1>(
                Action<ISandbox> logicToRun, [CallerFilePath] string callerFile = @"N/A",
                [CallerMemberName] string callerName = @"N/A", [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
        {
            Run
                <bool, TKnownException1, IgnoreException, IgnoreException, IgnoreException, IgnoreException>(sandbox =>
                {
                    logicToRun(sandbox);
                    return true;
                }, null, callerFile, callerName, callerLine);
        }


        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        public static void Run(Action<ISandbox> logicToRun, [CallerFilePath] string callerFile = @"N/A",
            [CallerMemberName] string callerName = @"N/A", [CallerLineNumber] int callerLine = 0)
        {
            Run<bool, IgnoreException, IgnoreException, IgnoreException, IgnoreException, IgnoreException>(sandbox =>
            {
                logicToRun(sandbox);
                return true;
            }, null, callerFile, callerName, callerLine);
        }

        #endregion

        #region Delegates with returns

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="logicOnException">the code block to run in case of exception</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TKnownException2">Second known exception</typeparam>
        /// <typeparam name="TKnownException3">Third known exception</typeparam>
        /// <typeparam name="TKnownException4">Forth known exception</typeparam>
        /// <typeparam name="TKnownException5">Fifth known exception</typeparam>
        /// <typeparam name="TReturn">The return type</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        private static TReturn Run
            <TReturn, TKnownException1, TKnownException2, TKnownException3, TKnownException4, TKnownException5>(
                Func<ISandbox, TReturn> logicToRun, Action<Exception> logicOnException, string callerFile,
                string callerName, int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
            where TKnownException3 : Exception
            where TKnownException4 : Exception
            where TKnownException5 : Exception
        {
            using (
                ISandbox sandbox = new ExecutionSandbox(ContextId, CallerRepProvider, callerFile, callerName, callerLine,
                    LogServer))
            {
                try
                {
                    if (!string.IsNullOrWhiteSpace(Company)) sandbox.Company = Company;
                    if (!string.IsNullOrWhiteSpace(Division)) sandbox.Division = Division;
                    if (!string.IsNullOrWhiteSpace(Application)) sandbox.Application = Application;
                    var returnValue = logicToRun(sandbox);
                    sandbox.LogReturnValue(returnValue);
                    sandbox.FinalStatus = SandboxState.Succeded;
                    return returnValue;
                }
                catch (TKnownException1 knownException1)
                {
                    sandbox.LogIgnoredException(knownException1);
                    sandbox.FinalStatus = SandboxState.SuceededWithWarning;
                    if (logicOnException != null) logicOnException(knownException1);
                    if (typeof(TReturn) == typeof(ISandbox)) return (TReturn) sandbox;
                }
                catch (TKnownException2 knownException2)
                {
                    sandbox.LogIgnoredException(knownException2);
                    sandbox.FinalStatus = SandboxState.SuceededWithWarning;
                    if (logicOnException != null) logicOnException(knownException2);
                    if (typeof(TReturn) == typeof(ISandbox)) return (TReturn) sandbox;
                }
                catch (TKnownException3 knownException3)
                {
                    sandbox.LogIgnoredException(knownException3);
                    sandbox.FinalStatus = SandboxState.SuceededWithWarning;
                    if (logicOnException != null) logicOnException(knownException3);
                    if (typeof(TReturn) == typeof(ISandbox)) return (TReturn) sandbox;
                }
                catch (TKnownException4 knownException4)
                {
                    sandbox.LogIgnoredException(knownException4);
                    sandbox.FinalStatus = SandboxState.SuceededWithWarning;
                    if (logicOnException != null) logicOnException(knownException4);
                    if (typeof(TReturn) == typeof(ISandbox)) return (TReturn) sandbox;
                }
                catch (TKnownException5 knownException5)
                {
                    sandbox.LogIgnoredException(knownException5);
                    sandbox.FinalStatus = SandboxState.SuceededWithWarning;
                    if (logicOnException != null) logicOnException(knownException5);
                    if (typeof(TReturn) == typeof(ISandbox)) return (TReturn) sandbox;
                }
                catch (OutOfMemoryException exception)
                {
                    sandbox.LogFailureException(exception);
                    sandbox.FinalStatus = SandboxState.EnvironmentFailure;
                    if (logicOnException != null) logicOnException(exception);
                    throw;
                }
                catch (ConfigurationException exception)
                {
                    sandbox.LogFailureException(exception);
                    sandbox.FinalStatus = SandboxState.ConfigurationFailure;
                    if (logicOnException != null) logicOnException(exception);
                    throw;
                }
                catch (ArgumentNullException exception)
                {
                    sandbox.LogFailureException(exception);
                    sandbox.FinalStatus = SandboxState.DataValidationFailure;
                    if (logicOnException != null) logicOnException(exception);
                    throw;
                }
                catch (InvalidDataException exception)
                {
                    sandbox.LogFailureException(exception);
                    sandbox.FinalStatus = SandboxState.DataValidationFailure;
                    if (logicOnException != null) logicOnException(exception);
                    throw;
                }
                catch (ArgumentOutOfRangeException exception)
                {
                    sandbox.LogFailureException(exception);
                    sandbox.FinalStatus = SandboxState.DataValidationFailure;
                    if (logicOnException != null) logicOnException(exception);
                    throw;
                }
                catch (DbException exception)
                {
                    sandbox.LogFailureException(exception);
                    sandbox.FinalStatus = SandboxState.DataSourceFailure;
                    if (logicOnException != null) logicOnException(exception);
                    throw;
                }
                catch (NullReferenceException exception)
                {
                    sandbox.LogFailureException(exception);
                    sandbox.FinalStatus = SandboxState.ApplicationFailure;
                    if (logicOnException != null) logicOnException(exception);
                    throw;
                }
                catch (Exception exception)
                {
                    sandbox.LogFailureException(exception);
                    sandbox.FinalStatus = SandboxState.UnknownFailure;
                    if (logicOnException != null) logicOnException(exception);
                    throw;
                }
            }
            return default(TReturn);
        }

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="logicOnException">the code block to run in case of exception</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TKnownException2">Second known exception</typeparam>
        /// <typeparam name="TKnownException3">Third known exception</typeparam>
        /// <typeparam name="TKnownException4">Forth known exception</typeparam>
        /// <typeparam name="TReturn">The return type</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        public static TReturn Run
            <TReturn, TKnownException1, TKnownException2, TKnownException3, TKnownException4>(
                Func<ISandbox, TReturn> logicToRun, Action<Exception> logicOnException,
                [CallerFilePath] string callerFile = @"N/A", [CallerMemberName] string callerName = @"N/A",
                [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
            where TKnownException3 : Exception
            where TKnownException4 : Exception
        {
            return
                Run
                    <TReturn, TKnownException1, TKnownException2, TKnownException3, TKnownException4, IgnoreException>(
                        logicToRun, logicOnException, callerFile, callerName, callerLine);
        }

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="logicOnException">the code block to run in case of exception</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TKnownException2">Second known exception</typeparam>
        /// <typeparam name="TKnownException3">Third known exception</typeparam>
        /// <typeparam name="TReturn">The return type</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        public static TReturn Run
            <TReturn, TKnownException1, TKnownException2, TKnownException3>(
                Func<ISandbox, TReturn> logicToRun, Action<Exception> logicOnException,
                [CallerFilePath] string callerFile = @"N/A", [CallerMemberName] string callerName = @"N/A",
                [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
            where TKnownException3 : Exception
        {
            return
                Run
                    <TReturn, TKnownException1, TKnownException2, TKnownException3, IgnoreException, IgnoreException>(
                        logicToRun, logicOnException, callerFile, callerName, callerLine);
        }

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="logicOnException">the code block to run in case of exception</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TKnownException2">Second known exception</typeparam>
        /// <typeparam name="TReturn">The return type</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        public static TReturn Run
            <TReturn, TKnownException1, TKnownException2>(
                Func<ISandbox, TReturn> logicToRun, Action<Exception> logicOnException,
                [CallerFilePath] string callerFile = @"N/A", [CallerMemberName] string callerName = @"N/A",
                [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
        {
            return
                Run
                    <TReturn, TKnownException1, TKnownException2, IgnoreException, IgnoreException, IgnoreException>(
                        logicToRun, logicOnException, callerFile, callerName, callerLine);
        }

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="logicOnException">the code block to run in case of exception</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TReturn">The return type</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        public static TReturn Run
            <TReturn, TKnownException1>(
                Func<ISandbox, TReturn> logicToRun, Action<Exception> logicOnException,
                [CallerFilePath] string callerFile = @"N/A", [CallerMemberName] string callerName = @"N/A",
                [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
        {
            return
                Run
                    <TReturn, TKnownException1, IgnoreException, IgnoreException, IgnoreException, IgnoreException>(
                        logicToRun, logicOnException, callerFile, callerName, callerLine);
        }

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="logicOnException">the code block to run in case of exception</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TReturn">The return type</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        public static TReturn Run
            <TReturn>(
                Func<ISandbox, TReturn> logicToRun, Action<Exception> logicOnException,
                [CallerFilePath] string callerFile = @"N/A", [CallerMemberName] string callerName = @"N/A",
                [CallerLineNumber] int callerLine = 0)
        {
            return
                Run
                    <TReturn, IgnoreException, IgnoreException, IgnoreException, IgnoreException, IgnoreException>(
                        logicToRun, logicOnException, callerFile, callerName, callerLine);
        }

        #endregion

        #region Delegates without return

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="logicOnException">the code block to run in case of exception</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TKnownException2">Second known exception</typeparam>
        /// <typeparam name="TKnownException3">Third known exception</typeparam>
        /// <typeparam name="TKnownException4">Forth known exception</typeparam>
        /// <typeparam name="TKnownException5">Fifth known exception</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        public static void Run
            <TKnownException1, TKnownException2, TKnownException3, TKnownException4, TKnownException5>(
                Action<ISandbox> logicToRun, Action<Exception> logicOnException,
                [CallerFilePath] string callerFile = @"N/A", [CallerMemberName] string callerName = @"N/A",
                [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
            where TKnownException3 : Exception
            where TKnownException4 : Exception
            where TKnownException5 : Exception
        {
            Run<bool, TKnownException1, TKnownException2, TKnownException3, TKnownException4, TKnownException5>(
                sandbox =>
                {
                    logicToRun(sandbox);
                    return true;
                }, logicOnException, callerFile, callerName, callerLine);
        }

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="logicOnException">the code block to run in case of exception</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TKnownException2">Second known exception</typeparam>
        /// <typeparam name="TKnownException3">Third known exception</typeparam>
        /// <typeparam name="TKnownException4">Forth known exception</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        public static void Run
            <TKnownException1, TKnownException2, TKnownException3, TKnownException4>(
                Action<ISandbox> logicToRun, Action<Exception> logicOnException,
                [CallerFilePath] string callerFile = @"N/A", [CallerMemberName] string callerName = @"N/A",
                [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
            where TKnownException3 : Exception
            where TKnownException4 : Exception
        {
            Run
                <bool, TKnownException1, TKnownException2, TKnownException3, TKnownException4, IgnoreException>(
                    sandbox =>
                    {
                        logicToRun(sandbox);
                        return true;
                    }, logicOnException, callerFile, callerName, callerLine);
        }

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="logicOnException">the code block to run in case of exception</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TKnownException2">Second known exception</typeparam>
        /// <typeparam name="TKnownException3">Third known exception</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        public static void Run
            <TKnownException1, TKnownException2, TKnownException3>(
                Action<ISandbox> logicToRun, Action<Exception> logicOnException,
                [CallerFilePath] string callerFile = @"N/A", [CallerMemberName] string callerName = @"N/A",
                [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
            where TKnownException3 : Exception
        {
            Run
                <bool, TKnownException1, TKnownException2, TKnownException3, IgnoreException, IgnoreException>(
                    sandbox =>
                    {
                        logicToRun(sandbox);
                        return true;
                    }, logicOnException, callerFile, callerName, callerLine);
        }

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="logicOnException">the code block to run in case of exception</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <typeparam name="TKnownException2">Second known exception</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        public static void Run
            <TKnownException1, TKnownException2>(
                Action<ISandbox> logicToRun, Action<Exception> logicOnException,
                [CallerFilePath] string callerFile = @"N/A", [CallerMemberName] string callerName = @"N/A",
                [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
            where TKnownException2 : Exception
        {
            Run
                <bool, TKnownException1, TKnownException2, IgnoreException, IgnoreException, IgnoreException>(sandbox =>
                {
                    logicToRun(sandbox);
                    return true;
                }, logicOnException, callerFile, callerName, callerLine);
        }

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="logicOnException">the code block to run in case of exception</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <typeparam name="TKnownException1">First known exception</typeparam>
        /// <returns>the returned object if returned by the code block.</returns>
        public static void Run
            <TKnownException1>(
                Action<ISandbox> logicToRun, Action<Exception> logicOnException,
                [CallerFilePath] string callerFile = @"N/A", [CallerMemberName] string callerName = @"N/A",
                [CallerLineNumber] int callerLine = 0)
            where TKnownException1 : Exception
        {
            Run
                <bool, TKnownException1, IgnoreException, IgnoreException, IgnoreException, IgnoreException>(sandbox =>
                {
                    logicToRun(sandbox);
                    return true;
                }, logicOnException, callerFile, callerName, callerLine);
        }

        /// <summary>
        /// Delegate to execute the code block
        /// </summary>
        /// <param name="logicToRun">the code block</param>
        /// <param name="logicOnException">the code block to run in case of exception</param>
        /// <param name="callerFile">optional code file name</param>
        /// <param name="callerName">optional calling method name</param>
        /// <param name="callerLine">optional calling method line number in callerFile</param>
        /// <returns>the returned object if returned by the code block.</returns>
        public static void Run(Action<ISandbox> logicToRun, Action<Exception> logicOnException,
            [CallerFilePath] string callerFile = @"N/A", [CallerMemberName] string callerName = @"N/A",
            [CallerLineNumber] int callerLine = 0)
        {
            Run<bool, IgnoreException, IgnoreException, IgnoreException, IgnoreException, IgnoreException>(sandbox =>
            {
                logicToRun(sandbox);
                return true;
            }, logicOnException, callerFile, callerName, callerLine);
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        public static ICallerAgentProvider CallerRepProvider { get; set; }

        internal static List<ISandbox> ActiveSandboxes
        {
            get
            {
                lock (LockObject)
                {
                    return _activeSandboxes ?? (_activeSandboxes = new List<ISandbox>());
                }
            }
            set { _activeSandboxes = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        public static bool SupressOutput
        {
            get
            {
                lock (LockObject)
                {
                    return _supressOutput;
                }
            }
            set { _supressOutput = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <exception cref="ValidationException"></exception>
        public static IValidator Ensure
        {
            get
            {
                var validator = _activeSandboxes.LastOrDefault();
                if (validator == null) throw new ValidationException("No active sandbox found.");
                return validator;
            }
        }

        public static IEnumerable<LogDestination> FailoverSequence { get; set; }

        internal static ReaderWriterLock LogWriteLock = new ReaderWriterLock();

        public static string LocalLogFile { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public static string Company { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public static string Division { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public static string Application { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public static string LogServer { get; set; }

        public static string RootAssemblyPrefix { get; set; }
        public static int RolloverSizeInMb { get; set; }
        public static LogMode LogMode { get; set; }
        public static LogDestination LogDestination { get; set; }
        public static Action<ILogData> CustomLogWriter { get; set; }

        public static ManagedSession Cache
        {
            get { return ManagedSession.Store; }
        }

        public static void RegisterMapException<TSource, TDestination>(Func<TSource, TDestination> mapping)
        {
            Cache.AddOrUpdate(string.Format("ExceptionsAs{0}To{1}", typeof(TSource), typeof(TDestination)), mapping,
                (s, o) => mapping);
        }


        public static TDestination GetMapException<TSource, TDestination>(TSource source)
        {
            var key = string.Format("ExceptionsAs{0}To{1}", typeof(TSource), typeof(TDestination));
            if (Cache.ContainsKey(key))
            {
                var exception = (Func<TSource, TDestination>) Cache[key];
                if (exception != null) return exception(source);
            }
            return default(TDestination);
        }

        #region Extensions

        public static object MapTo(this object value, Type type, object parentObject = null,
            params Expression<Func<object>>[] parameters)
        {
            if (value is DBNull) return type.IsValueType ? Activator.CreateInstance(type) : null;
            if (value.GetType() == type) return value;
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                var returnValue =
                    Activator.CreateInstance(typeof(Nullable<>).MakeGenericType(type.GetGenericArguments()[0]));
                if (!type.IsValueType && (value == null || string.IsNullOrEmpty(value as string)))
                    return type.IsValueType ? Activator.CreateInstance(type) : null;
                returnValue = Convert.ChangeType(value, type.GetGenericArguments()[0], CultureInfo.InvariantCulture);
                return returnValue;
            }

            //if (type == typeof(bool)) return value == "T";
            object retVal = null;
            if (!value.GetType().IsValueType && !type.IsValueType && type.GetConstructor(Type.EmptyTypes) != null)
                retVal = DependencyLibrary.RunGenericMethod(typeof(MappingEngine), MappingEngine.Instance, "Map",
                    new[] {value.GetType(), type}, value, Activator.CreateInstance(type), parentObject ?? typeof(object),
                    parameters);
            //var retVal = MappingEngine.Instance.Map(value, Activator.CreateInstance(type));
            //retVal = DependencyLibrary.RunGenericMethod(typeof(Sandbox), null, "GetMapException",
            //    new[] {value.GetType(), type},value);

            var key = string.Format("ExceptionsAs{0}To{1}", value.GetType(), type);
            if (Cache.ContainsKey(key))
            {
                var exception = Cache[key];
                if (exception != null) retVal = ((Delegate) exception).DynamicInvoke(value);
            }

            if (retVal == null) return Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
            return retVal;
        }

        public static TDestination MapTo<TDestination>(this object value, object parentObject = null,
            params Expression<Func<object>>[] parameters)
        {
            return (TDestination) MapTo(value, typeof(TDestination), parentObject, parameters);
        }

        public static TDestination SerializeTo<TDestination>(this object obj, Type extractInterface = null)
        {
            if (obj == null) return default(TDestination);
            List<object> stack = new List<object>();
            if (typeof(TDestination) == typeof(string))
                return SerializeOneObjToString(obj, null,stack, extractInterface).MapTo<TDestination>();
            stack.Clear();
            return obj.MapTo<TDestination>();
        }

        private static string SerializeOneObjToString(object obj, object parent,List<object> stack, Type extractInterface = null)
        {
            using (var writer = new StringWriter())
            {
                try
                {
                    if (obj == null) return string.Empty;
                    if (stack.Any(o=> o==obj && ReferenceEquals(o,obj))) return string.Empty;
                    stack.Add(obj);
                    var thisType = extractInterface ?? obj.GetType();
                    writer.Write("<{0}>", obj.GetType());
                    if (obj.GetType() == typeof(DataSet))
                    {
                        writer.Write("<Value>{0}</Value>", ((DataSet)obj).GetXml());
                        writer.Write("</{0}>", obj.GetType());
                        return writer.ToString();
                    }
                    if (obj.GetType() == typeof(DataTable))
                    {
                        writer.Write("<Value>{0}</Value>", ((DataTable)obj).DataSet.GetXml());
                        writer.Write("</{0}>", obj.GetType());
                        return writer.ToString();
                    }
                    if (obj.GetType().IsValueType || obj is string || obj is Exception)
                    {
                        writer.Write("<Value>{0}</Value>", obj);//string.Join(Environment.NewLine, obj.ToString().Split(new[] { typeof(Sandbox).Namespace }, StringSplitOptions.RemoveEmptyEntries).Where(s => !s.StartsWith(typeof(Sandbox).Namespace)))
                        writer.Write("</{0}>", obj.GetType());
                        return writer.ToString();
                    }
                    if (obj is IEnumerable)
                    {
                        foreach (var eachObj in ((IEnumerable)obj))
                        {
                            writer.Write(SerializeOneObjToString(eachObj, parent,stack));
                        }
                        writer.Write("</{0}>", obj.GetType());
                        return writer.ToString();
                    }

                    //if(thisType.IsValueType && thisType.Is)
                    foreach (var property in thisType.GetProperties())
                    {
                        writer.Write("<{0}>", property.Name);
                        var value = property.GetValue(obj, null);
                        if (value!=null && value.GetType() == obj.GetType()) continue;
                        //if(ReferenceEquals(value,obj))
                        try { writer.WriteLine(SerializeOneObjToString(value, obj,stack)); }
                        catch (Exception ex) { writer.WriteLine("<ERROR>Error reading {0}: {1}</ERROR>", property.Name, ex); }
                        writer.Write("</{0}>", property.Name);
                    }
                    writer.Write("</{0}>", obj.GetType());
                }
                catch (Exception ex)
                {
                    writer.Write("<UnExpectedFailure>{0}</UnExpectedFailure>", ex);
                }
                return writer.ToString();
            }
        }


        internal static bool IsDeepInterfaced<TOfType>(this Type typeToAssign)
        {
            var isAssignable = typeToAssign.GetInterfaces().Any(i => i is TOfType);
            if (isAssignable) return true;
            if (typeToAssign.BaseType != null) return IsDeepInterfaced<TOfType>(typeToAssign.BaseType);
            return false;
        }

        internal static bool IsDeepInHerited<TOfType>(this Type typeToAssign)
        {
            return IsDeepInHerited(typeToAssign, typeof(TOfType));
            //var isInherited = typeToAssign.BaseType == typeof(TOfType);
            //if (isInherited) return true;
            //if (typeToAssign.BaseType != null) return IsDeepInHerited<TOfType>(typeToAssign.BaseType);
            //return false;
        }

        internal static bool IsDeepInHerited(this Type typeToAssign, Type typeCheckType)
        {
            var isInherited = typeToAssign.BaseType!=null && typeToAssign.BaseType.FullName == typeCheckType.FullName;
            if (isInherited) return true;
            if (typeToAssign.BaseType != null) return IsDeepInHerited(typeToAssign.BaseType, typeCheckType);
            return false;
        }
        #endregion

    }

    /// <summary>
    /// Represents which never occurs :)
    /// </summary>
    public class IgnoreException : Exception
    {
    }
}